package cn.aws360.boot.system.biz.helper;

import cn.aws360.boot.system.api.constant.enums.BucketEnum;
import cn.aws360.boot.system.api.pojo.properties.FileProperties;
import cn.hutool.core.util.ObjectUtil;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.util.IOUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;

import java.io.ByteArrayInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author junnan
 */
@Slf4j
@RequiredArgsConstructor
public class OssHelper implements InitializingBean {

    private AmazonS3 amazonS3;
    private final FileProperties fileProperties;
    private static final String PUBLIC_POLICY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AddPerm\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::{bucketName}/*\"]}]}";

    @Override
    public void afterPropertiesSet() {
        AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
                fileProperties.getEndpoint(), fileProperties.getRegion());

        AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(new BasicAWSCredentials(
                fileProperties.getAccessKey(), fileProperties.getSecretKey()));

        ClientConfiguration clientConfiguration = new ClientConfiguration();
        clientConfiguration.setMaxConnections(fileProperties.getMaxConnections());

        this.amazonS3 = AmazonS3Client.builder()
                .withEndpointConfiguration(endpointConfiguration)
                .withCredentials(awsCredentialsProvider)
                .withClientConfiguration(clientConfiguration)
                .disableChunkedEncoding()
                .withPathStyleAccessEnabled(fileProperties.getPathStyleAccess()).build();

        initBucket();
    }

    /**
     * 初始化存储桶
     */
    public void initBucket() {
        for (BucketEnum bucket : BucketEnum.values()) {
            try {
                String bucketName = bucket.getName();
                createBucket(bucketName);
                if (bucket.isPub()) {
                    setBucketPolicyPublic(bucketName);
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 创建存储桶
     *
     * @param bucketName 存储桶
     */
    public void createBucket(String bucketName) {
        try {
            boolean isExist = amazonS3.doesBucketExistV2(bucketName);
            if (!isExist) {
                amazonS3.createBucket((bucketName));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 设置存储桶公有
     *
     * @param bucketName 存储桶
     */
    public void setBucketPolicyPublic(String bucketName) {
        try {
            String policyText = PUBLIC_POLICY.replace("{bucketName}", bucketName);
            amazonS3.setBucketPolicy(bucketName, policyText);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 列出桶内对象
     *
     * @param bucketName 桶名称
     * @param prefix     对象前缀
     * @return List<S3ObjectSummary>
     */
    public List<S3ObjectSummary> listObject(String bucketName, String prefix) {
        try {
            ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
            return new ArrayList<>(objectListing.getObjectSummaries());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return new ArrayList<>();
    }

    /**
     * 上传对象文件
     *
     * @param bucketName  桶名称
     * @param objectName  对象名
     * @param bytes       对象字节
     * @param contentType 文件类型
     * @return 是否成功
     */
    public boolean putObject(String bucketName, String objectName, byte[] bytes, String contentType) {
        try {
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentType(contentType);
            objectMetadata.setContentLength(bytes.length);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    /**
     * 获取对象文件字节
     *
     * @param bucketName 桶名称
     * @param objectName 对象名
     * @return S3Object
     */
    public byte[] getObject(String bucketName, String objectName) {
        try {
            S3Object object = amazonS3.getObject(bucketName, objectName);
            if (ObjectUtil.isNotNull(object)) {
                return IOUtils.toByteArray(object.getObjectContent());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 获取对象文件链接
     *
     * @param bucketName 桶名称
     * @param objectName 对象名
     * @param expire     过期时间，null(公有)、>0(私有，毫秒数，最大7天)
     * @return 文件链接
     */
    public String getObjectUrl(String bucketName, String objectName, Integer expire) {
        try {
            URL url;
            if (ObjectUtil.isNull(expire)) {
                url = amazonS3.getUrl(bucketName, objectName);
            } else {
                expire = Math.min(expire, 7 * 24 * 60 * 60);
                Date expireDate = new Date(System.currentTimeMillis() + expire);
                url = amazonS3.generatePresignedUrl(bucketName, objectName, expireDate);
            }
            return ObjectUtil.isNotNull(url) ? url.toString() : null;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 删除对象文件
     *
     * @param bucketName 桶名称
     * @param objectName 对象名
     * @return 是否成功
     */
    public boolean delObject(String bucketName, String objectName) {
        try {
            amazonS3.deleteObject(bucketName, objectName);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    /**
     * 强制删除桶
     *
     * @param bucketName 桶名称
     * @return 是否成功
     */
    public boolean forceDeleteBucket(String bucketName) {
        try {
            List<S3ObjectSummary> listObject = listObject(bucketName, null);
            for (S3ObjectSummary objectSummary : listObject) {
                String objectName = objectSummary.getKey();
                amazonS3.deleteObject(bucketName, objectName);
            }
            amazonS3.deleteBucket(bucketName);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

}
